package net.sf.odinms.exttools.doclet.pages;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.PackageDoc;
import com.sun.tools.doclets.internal.toolkit.util.Util;
import net.sf.odinms.exttools.doclet.AbstractPageWriter;

/**
 * Contains functionality common to hierarchy generation classes.
 */
@SuppressWarnings("unchecked")
public abstract class AbstractTree extends AbstractPageWriter {
	
	/**
	 * Packages in this run.
	 */
	protected PackageDoc[] packages;
	
	/**
	 * True if there are no packages specified on the command line, False
	 * otherwise.
	 */
	protected boolean classesOnly;
	
	private static int indent = 0;
	
	/** Localized strings for hierarchy headings and id tags. */
	private static enum Hierarchy {
		INTERFACES(CONF.propertyText("Interface_Hierarchy"),
				CONF.propertyText("Interfaces")),
		CLASSES(CONF.propertyText("Class_Hierarchy"),
				CONF.propertyText("Classes")),
		EXCEPTIONS(CONF.propertyText("Exception_Hierarchy"),
				CONF.propertyText("Exceptions")),
		ERRORS(CONF.propertyText("Error_Hierarchy"),
				CONF.propertyText("Errors")),
		ENUMS(CONF.propertyText("Enum_Hierarchy"), CONF.propertyText("Enums")),
		ANNOTATION_TYPES(CONF.propertyText("Annotation_Type_Hierarchy"),
				CONF.propertyText("AnnotationTypes"));
		
		private String heading;
		private String id;
		
		/**
		 * Private constructor for enum items.
		 * 
		 * @param heading The text to include in the hierarchy section heading.
		 * @param id The ID value to include in anchors for links within page.
		 */
		private Hierarchy(String heading, String id) {
			this.heading = heading;
			this.id = id;
		}
	}
	
	/**
	 * Initialize the filename and packages to include in hierarchy.
	 * 
	 * @param filename The desired name of the file (with extension).
	 * @throws IOException If {@link java.io.FileOutputStream} creation fails.
	 */
	protected AbstractTree(String filename) throws IOException {
		this(filename, "");
	}
	
	/**
	 * Initialize the filename and packages to include in hierarchy, plus path
	 * from document root.
	 * 
	 * @param filename The desired name of the file (with extension).
	 * @param pathToFile The path from the document root to this file.
	 * @throws IOException If {@link java.io.FileOutputStream} creation fails.
	 */
	protected AbstractTree(String filename, String pathToFile)
			throws IOException {
		super(filename, pathToFile);
		packages = CONF.packages;
		classesOnly = (packages.length == 0);
	}
	
	/**
	 * Shared tree generation behavior between OverviewTree and PackageTree.
	 * Prints listings for classes, interfaces, enums, and annotation types.
	 */
	// TODO See if errors and exceptions can be broken out in their own groups
	protected void printMemberSubgroups() {
		println(openDivWithID("Summaries"));
		generateTree(CONF.classTree.baseclasses(), Hierarchy.CLASSES);
		generateTree(CONF.classTree.baseinterfaces(), Hierarchy.INTERFACES);
		generateTree(CONF.classTree.baseEnums(), Hierarchy.ENUMS);
		generateTree(CONF.classTree.baseAnnotationTypes(),
				Hierarchy.ANNOTATION_TYPES);
		println(close("div") + getComment("#Summaries"));
	}
	
	/**
	 * Generate the heading for the tree depending upon tree type if it's a
	 * Class Tree or Interface tree and also print the tree.
	 * 
	 * @param list List of classes which are at the most base level, all the
	 *        other classes in this run will derive from these classes.
	 * @param section Object containing the heading and ID for this section
	 */
	private void generateTree(List list, Hierarchy section) {
		if (list.size() > 0) {
			ClassDoc firstClassDoc = (ClassDoc) list.get(0);
			println(new TagBuilder("h2").add("id", section.id).getOpenTextClose(
					section.heading));
			indent = 0;
			generateLevelInfo(!firstClassDoc.isInterface()
					? firstClassDoc
					: null, list, list == CONF.classTree.baseEnums());
			println();
		}
	}
	
	/**
	 * Generate each level of the class tree. For each sub-class or
	 * sub-interface indents the next level information. Recurses itself to
	 * generate subclasses info. To iterate is human, to recurse is divine - L.
	 * Peter Deutsch.
	 * 
	 * @param parent The superclass or superinterface of the list.
	 * @param list List of the sub-classes at this level.
	 * @param isEnum True if we are generating a tree for enums.
	 */
	private void generateLevelInfo(ClassDoc parent, List list, boolean isEnum) {
		if (list.size() > 0) {
			println(open("ul"));
			indent += 2;
			for (int i = 0; i < list.size(); i++) {
				ClassDoc local = (ClassDoc) list.get(i);
				print(spaces(indent, false) + open("li"));
				printPartialInfo(local);
				printExtendsImplements(parent, local);
				generateLevelInfo(local, CONF.classTree.subs(local, isEnum),
						isEnum); // Recurse
				println(close("li"));
			}
			indent -= 2;
			print(spaces(indent, false) + close("ul"));
		}
	}
	
	/**
	 * Print information about the class kind, if it's a "class" or "interface".
	 * 
	 * @param cd classdoc.
	 */
	private void printPartialInfo(ClassDoc cd) {
		// TODO Print class parameters within tree
		print(cd.containingPackage() + "." + linkToClass(cd, false));
	}
	
	/**
	 * Print the information regarding the classes which this class extends or
	 * implements.
	 * 
	 * @param parent The superclass or superinterface of the current class.
	 * @param cd The classdoc under consideration.
	 */
	private void printExtendsImplements(ClassDoc parent, ClassDoc cd) {
		ClassDoc[] interfaces = cd.interfaces();
		if (interfaces.length > (cd.isInterface() ? 1 : 0)) {
			Arrays.sort(interfaces);
			int counter = 0;
			for (ClassDoc iface : interfaces) {
				if (parent != iface) {
					if (!(iface.isPublic() || Util.isLinkable(iface, CONF))) {
						continue;
					}
					if (counter++ == 0) {
						if (cd.isInterface()) {
							print(" (" + CONF.propertyText("doclet.also")
									+ " extends ");
						} else {
							print(" (implements ");
						}
					} else {
						print(", ");
					}
					print(iface.containingPackage() + "."
							+ linkToClass(iface, false));
					// printPreQualifiedClassLink(LinkInfoImpl.CONTEXT_TREE,
					// iface);
				}
			}
			if (counter > 0) {
				print(")");
			}
		}
	}
	
	/** Print links to Summary sections for package elements. */
	protected void navWithinPage() {
		List<String> summaries = new Vector<String>();
		if (CONF.classTree.baseclasses().size() > 0)
			summaries.add(linkToLabelHref("Classes", "#Classes"));
		if (CONF.classTree.baseinterfaces().size() > 0)
			summaries.add(linkToLabelHref("Interfaces", "#Interfaces"));
		if (CONF.classTree.baseEnums().size() > 0)
			summaries.add(linkToLabelHref("Enums", "#Enums"));
		if (CONF.classTree.baseAnnotationTypes().size() > 0)
			summaries.add(linkToLabelHref("Annotation Types",
					"#AnnotationTypes"));
		print(open("td id=\"WithinPage\""));
		printUnorderedListWithLast(summaries);
		println(close("td"));
	}
	
	/** Highlight "Tree" as current section, don't create link. */
	protected void navLinkTree() {
		println(listItemCurrent(TREE));
	}
	
	
}
